﻿namespace Kodefu.Mvc
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Web;
    using System.Web.Mvc;

    public class FuzzyController : Controller
    {
        protected override void HandleUnknownAction(string actionName)
        {
            if (!HttpContext.Request.HttpMethod.Equals("GET", StringComparison.OrdinalIgnoreCase))
                throw new HttpException(404, actionName);

            TryToRedirectToAnActionNearby(actionName);
        }

        private void TryToRedirectToAnActionNearby(string actionName)
        {
            var actionDistanceMap = ActionNames().Select(action => new { Key = action, Value = action.DistanceTo(actionName) })
                                                       .Where(i => i.Value <= 3);
            if (!actionDistanceMap.Any())
                throw new HttpException(404, actionName);

            var shortestDistance = actionDistanceMap.Select(v => v.Value).Min();
            var nearestAction = actionDistanceMap.Where(i => i.Value == shortestDistance).First().Key;

            ControllerContext.RouteData.Values["action"] = nearestAction;

            new RedirectResult(Url.RouteUrl(RouteData.Values), permanent: true)
                .ExecuteResult(ControllerContext);
        }

        private IEnumerable<string> ActionNames()
        {
            return GetType()
                    .GetMethods(BindingFlags.InvokeMethod |
                                BindingFlags.Public |
                                BindingFlags.Instance)
                    .Where(m => m.ReturnType == typeof(ActionResult) &&
                                !m.IsSpecialName &&
                                !m.GetCustomAttributes(true)
                                    .Contains(typeof(HttpPostAttribute)))
                    .Select(m => m.Name)
                    .Distinct();
        }       
    }
}
